﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace QQ2564874169.RelationalSql
{
    public interface IPersister : IDisposable
    {
        void Save(IEnumerable<PersistData> data);
        void Remove(PersistData data);
        IEnumerable<PersistData> Load();
    }

    public class LocalFilePersister : IPersister
    {
        public interface ISerialization
        {
            object Deserialize(Type type, string data);
            string Serialize(object obj);
        }
        private string _dir;
        private ISerialization _serialization;

        public LocalFilePersister(ISerialization serialization, string dir = "DbExecuteMonitor")
        {
            _dir = dir;
            _serialization = serialization;
        }

        public void Save(IEnumerable<PersistData> data)
        {
            var date = DateTime.Now.ToString("yyyyMMdd");
            var dir = Path.Combine(_dir, date);
            if (Directory.Exists(dir) == false)
            {
                Directory.CreateDirectory(dir);
            }
            Parallel.ForEach(data, item =>
            {
                var obj = new JsonPersistData
                {
                    Id = item.Id.ToString().ToLower(),
                    Operation = item.Operation,
                    CreateTime = item.CreateTime,
                    ServiceType = item.ServiceType.AssemblyQualifiedName,
                    TableType = item.TableType.AssemblyQualifiedName,
                    ModelType = item.Model.GetType().AssemblyQualifiedName,
                    Model = _serialization.Serialize(item.Model)
                };
                var serdata = _serialization.Serialize(obj);
                var path = Path.Combine(_dir, date, obj.Id + ".pd");
                File.WriteAllText(path, serdata);
            });
        }

        public void Remove(PersistData data)
        {
            var date = new DateTime(data.CreateTime);
            var path = Path.Combine(_dir, date.ToString("yyyyMMdd"), data.Id.ToString().ToLower() + ".pd");
            File.Delete(path);
        }

        public IEnumerable<PersistData> Load()
        {
            if (Directory.Exists(_dir) == false)
                return null;

            var dirs = Directory.GetDirectories(_dir);
            var list = new List<PersistData>();

            Parallel.ForEach(dirs, dir =>
            {
                Parallel.ForEach(Directory.GetFiles(dir, "*.pd"), file =>
                {
                    var pdata = File.ReadAllText(file);
                    var obj = (JsonPersistData) _serialization.Deserialize(typeof(JsonPersistData), pdata);
                    if (obj == null)
                        return;
                    var st = Type.GetType(obj.ServiceType, false);
                    if (st == null)
                        return;
                    var tt = Type.GetType(obj.TableType, false);
                    if (tt == null)
                        return;
                    var mt = Type.GetType(obj.ModelType, false);
                    if (mt == null)
                        return;
                    var model = _serialization.Deserialize(mt, obj.Model);
                    if (model == null)
                        return;
                    lock (list)
                    {
                        list.Add(new PersistData
                        {
                            Id = Guid.Parse(obj.Id),
                            CreateTime = obj.CreateTime,
                            Operation = obj.Operation,
                            ServiceType = st,
                            TableType = tt,
                            Model = model
                        });
                    }
                });
            });
            return list;
        }

        private class JsonPersistData
        {
            public string Id { get; set; }
            public DbExecuteOperation Operation { get; set; }
            public long CreateTime { get; set; }
            public string ServiceType { get; set; }
            public string TableType { get; set; }
            public string ModelType { get; set; }
            public string Model { get; set; }
        }

        public void Dispose()
        {
            
        }
    }
}
